#include "ifftgpu.h"

#include "primes.h"
#include "dims.h"

// Plan Creation (i.e. Solver) Codes

CTSolver::CTSolver() : Solver()
{
    n1 = 0;
}

CTSolver::~CTSolver()
{
}

bool CTSolver::applicable(Problem * prob)
{
    if (prob->fftDim.size() != 1) return false;

    n1 = get_radix(n1, prob->fftDim[0]);
    if (n1 <=1 ) return false;

    return true;
}

Plan * CTSolver::make_plan(Problem * prob)
{
    n1 = 0;
    return make_next_plan(prob);
}

Plan * CTSolver::make_next_plan(Problem * prob)
{
    bool ret;
    CTPlan * p = NULL;

    if (!applicable(prob)) {
        return NULL;
    }

    int n = prob->fftDim[0];
    int n2 = prob->fftDim[0] / n1;
    Dim dim0, dim1;

    Problem * prob1 = new Problem();
    Problem * prob2 = new Problem();
    
// Generate first problem
    prob1->fftDim.push_back(n1);
    prob1->iDim = prob->iDim;

    ret = dims_split(prob1->iDim, 0, n);
    if (!ret) return NULL;

    dim0 = prob1->iDim.front();
    prob1->iDim.pop_front();
    dim1 = Dim(n2, dim0.stride);
    dim0 = Dim(n1, n2 * dim0.stride);

    prob1->iDim.push_front(dim1);
    prob1->iDim.push_front(dim0);

    prob1->oDim = prob->oDim;

// TODO: TWIDDLE

// Generate second problem
    prob2->fftDim.push_back(n2);
    prob2->iDim = prob->iDim;

    ret = dims_split(prob2->iDim, 0, n);
    if (!ret) return NULL;

    dim0 = prob2->iDim.front();
    prob2->iDim.pop_front();
    dim1 = Dim(n1, prob->oDim.front().stride);
    dim0 = Dim(n2, n1 * prob->oDim.front().stride);

    prob2->iDim.push_front(dim1);
    prob2->iDim.push_front(dim0);

    prob2->oDim = prob->oDim;

    ret = dims_split(prob2->oDim, 0, n);
    if (!ret) return NULL;

    dim0 = prob2->oDim.front();
    prob2->oDim.pop_front();
    dim1 = Dim(n1, dim0.stride);
    dim0 = Dim(n2, n1 * dim0.stride);

    prob2->oDim.push_front(dim1);
    prob2->oDim.push_front(dim0);

    p = new CTPlan();
    p->n1plan = ::make_plan(prob1);
    p->n2plan = ::make_plan(prob2);

    p->n1 = n1;
    p->n2 = n2;

    p->pass = p->n1plan->pass + p->n2plan->pass;
    p->cost = p->n1plan->cost + p->n2plan->cost;

    SAFE_DELETE(prob1);
    SAFE_DELETE(prob2);

    return p;
}

// Plan Solving Codes

CTPlan::CTPlan():n1plan(NULL),n2plan(NULL),Plan() 
{
}

CTPlan::~CTPlan()
{
    SAFE_DELETE(n1plan);
    SAFE_DELETE(n2plan);
}


void CTPlan::awake(Wakefulness w)
{
    return;
}

void CTPlan::print(FILE *fp, int indent) 
{
    for (int i = indent; i > 0; i--)
        fprintf(fp, "    ");

    fprintf(fp, "(dft-ct-(%d x %d) \n",
        n1, n2 );
    
    n1plan->print(fp, indent + 1);
    n2plan->print(fp, indent + 1);

    for (int i = indent; i > 0; i--)
        fprintf(fp, "    ");
    fprintf(fp, ") \n");
    return;
}

void CTPlan::solve()
{
    return;
}